#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <stdint.h>

#include <security/pam_appl.h>
#include <security/pam_misc.h>
#include <security/pam_ext.h>

#include "read_json_db.h"
#include "sha512.h"
#include "base64.h"
#include "debug.h"

//in bytes
#define PASSWORD_SIZE    64
#define LOGIN_SIZE       64
#define SEED_SIZE        64
//in base64 size
#define PASSWORD64_SIZE  88
#define LOGIN64_SIZE     88
#define SEED64_SIZE      88


#define MSG_TYPE_USER 1
#define MSG_TYPE_SEED 2
#define MSG_TYPE_PWD  3

uint8_t password_buf[PASSWORD_SIZE+1];
uint8_t login_buf[LOGIN_SIZE+1];
uint8_t seed_buf[SEED_SIZE+1];
uint8_t password64_buf[PASSWORD64_SIZE+1];
uint8_t login64_buf[LOGIN64_SIZE+1];
uint8_t seed64_buf[SEED64_SIZE+1];
int password_read=0;
int password_prepared=0;

static int
test_conv (int num_msg , const struct pam_message **msgm ,
	   struct pam_response **response, void *appdata_ptr )
{
	int i=0;
	//int ret;
	printf("test_conv %d %8p %8p %8p\n",
		num_msg, (void *)msgm, (void *)response, (void *)appdata_ptr);
	for ( i=0; i<num_msg;i++ )
	{

		printf("msg %d \"%s\"\n", msgm[0]->msg_style, msgm[0]->msg);
		struct pam_response ps;

		switch ( msgm[i]->msg_style )
		{
			case MSG_TYPE_USER:
				ERROR("Not yet implemented\n");
				break;

			case MSG_TYPE_SEED:
				if ( strlen(msgm[i]->msg) == SEED64_SIZE )
				{
					memcpy( seed64_buf, msgm[i]->msg, SEED64_SIZE );
					seed64_buf[SEED64_SIZE] = 0x0;
					if ( password_read != 1)
					{
						ERROR("Asking for seed but password not yet readed\n");
					} else
					{
						PRINT("SEED %s\n", seed64_buf);
						unsigned char buf[SEED_SIZE+PASSWORD_SIZE]; memset( buf, 0, SEED_SIZE+PASSWORD_SIZE );
						memcpy( buf, password_buf, sizeof(password_buf) );
						PRINT("Buf:\n");
						print_hex( buf, 128, 16 );
						size_t dlen=SEED64_SIZE, slen=SEED64_SIZE;
						base64_decode( &buf[SEED_SIZE], &dlen, (const unsigned char*)msgm[i]->msg, slen );
						PRINT("Buf:\n");
						print_hex( buf, 128, 16 );
						unsigned char sha[SEED_SIZE];
						sha512( buf, SEED_SIZE+PASSWORD_SIZE, sha, 0 );
						PRINT("Hash(%d+%d):\n",SEED_SIZE,PASSWORD_SIZE);
						print_hex( sha, 64, 16 );
						dlen = 89;
						base64_encode( password64_buf, &dlen, sha, SEED_SIZE );
						PRINT("%s\n",password64_buf);
						password_prepared = 1;
						//*response = send_response( MSG_TYPE_USER, "seed" );
						ps.resp_retcode = MSG_TYPE_USER;
						ps.resp = "seed";
						*response = &ps;
					}


				} else
				{
					ERROR("Seed side not like base64 size %zu\n",strlen( msgm[i]->msg));
				}
				break;
			
			case MSG_TYPE_PWD:
				if ( strncmp( msgm[i]->msg, "password", 8 ) == 0 )
				{
					//*response = send_response( MSG_TYPE_PWD, password64_buf );
					ps.resp_retcode = MSG_TYPE_PWD;
					ps.resp = (char *)password64_buf;
				}
				break;
			
			default:
				ERROR("Unknown message\n");
		}
	}
    return 0; 
}

void cleanup( pam_handle_t *pamh, void *data, int error_status )
{
	printf("cealnup\n");
}


static struct pam_conv conv =
{
	test_conv,
	NULL
};


int main( int argc, char **argv )
{
	//init global variables
	memset( password_buf, 0, sizeof(password_buf) );
	memset( login_buf, 0, sizeof(login_buf) );
	memset( seed_buf, 0, sizeof(seed_buf) );

	char *user="johny";
	pam_handle_t *pamh=NULL;
	int ret=0,res=0;

	//struct termios std;
	struct termios old_std;

	
	tcgetattr( STDOUT_FILENO, &old_std );

	//login window
	write( 1, "Login:", 7 );
	ret = read( 1, login_buf, LOGIN_SIZE );
	login_buf[ret-1]=0x0;
	printf("%d=[%s]\n",ret,login_buf);
	print_hex( login_buf, 32, 16 );

	write(1,"Password:", 10);
	ret = read( 1, password_buf, PASSWORD_SIZE );
	password_buf[ret-1]=0x0;
	password_read = 1;
	printf("%d=[%s]\n",ret,password_buf);
	print_hex( password_buf, 32, 16 );

	if ( pam_start("ha_login", user, &conv, &pamh) != PAM_SUCCESS )
	{
		printf("Cannot start pam\n");
		return -1;
	}
	
	pam_set_item( pamh, PAM_USER, login_buf );
	//pam_set_data( pamh, "login", (void *)login_buf, &cleanup );

	//pam_set_item( pamh, PAM_AUTHTOK, password_buf );

	if ( pam_authenticate(pamh, 0) != PAM_SUCCESS )
	{
		PRINT("Cannot login\n");
		goto error_exit;
	}
	PRINT("Loged in\n");


error_exit:
	tcsetattr( STDOUT_FILENO, TCSANOW, &old_std );
	pam_end( pamh, res );

	return 0;
}

